package model;

import entity.TaskInfo;
import exceptions.DependencyException;
import graph.dependencies.IDependency;
import graph.visitors.DependencyCollector;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Observer;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import java.util.Observable;

import refactorplugin.views.CoreError;
import refactorplugin.views.INotificationObject;
import refactorplugin.views.TaskTreeRows;
import refactorplugin.views.TreeComponent;
import refactorplugin.views.TreeLeaf;
import refactorplugin.views.TreeComposite;
import tasks.ITask;
import tasks.TaskPool;

public class CodeRefactoringModel extends Observable implements IModel, Observer {
	
	private static final String _logFile = "reflog.log"; 
	
	private void setOutputFile(String fileName) {
		try {
			System.setOut(new PrintStream(new FileOutputStream(fileName)));
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
		}
	}
	
	private void analyzeCompilationUnits(IProject project) throws CoreException,
	JavaModelException {
		DependencyCollector dependencyCollector = new DependencyCollector();
		dependencyCollector.clearInstances();
		if (project.isNatureEnabled("org.eclipse.jdt.core.javanature")) {
			IJavaProject javaProject = JavaCore.create(project);
			for (IPackageFragment mypackage : javaProject.getPackageFragments()) {
				if (mypackage.getKind() == IPackageFragmentRoot.K_SOURCE) {
					for (ICompilationUnit unit : mypackage.getCompilationUnits()) {
						System.out.println("CodeRefactoringModel>Analyzing "+unit.getElementName());
						parse(unit).accept(dependencyCollector);
					}
				}
			}
			CoreError errors = new CoreError();
			for (IDependency dependency:dependencyCollector.getDependencies())
				try {
					dependency.scheduleFixTask();
				} catch (DependencyException e) {
					errors.addError(e);
				}
			if (!errors.isEmpty()) {
				setChanged();
				notifyObservers(errors);
			}
		}
	}
	
	private CompilationUnit parse(ICompilationUnit unit) {
		ASTParser parser = ASTParser.newParser(AST.JLS3); 
		parser.setKind(ASTParser.K_COMPILATION_UNIT);
		parser.setSource(unit);
		parser.setEnvironment(null, null, null, true);
		parser.setResolveBindings(true);
		return (CompilationUnit) parser.createAST(null);
	}
	
	public CodeRefactoringModel() {
		setOutputFile(_logFile);
	}
	
	public void updateNotify() {
		update();
		setChanged();
		List<TaskInfo> rows = getRows();
		System.out.println("UPDATE TASKS="+rows);
		for(TaskInfo tI:rows)
			System.out.println(tI.getDescription());
		notifyObservers(new TaskTreeRows(rows));
	}
	
	public void updateNotify(INotificationObject notificationObject) {
		update();
		setChanged();
		notifyObservers(notificationObject);//getRows());
	}

	private void update() {
		System.out.println("CodeRefactoringModel>Update tasks");
		dropOldActiveTasks();
		// Get the root of the workspace
		IWorkspace workspace = ResourcesPlugin.getWorkspace();
		IWorkspaceRoot root = workspace.getRoot();
		// Get all projects in the workspace
		IProject[] projects = root.getProjects();
		/** Loop over all projects */
		for (IProject project : projects) {
			try {
				//printProjectInfo(project);
				analyzeCompilationUnits(project);
			} catch (CoreException e) {
				e.printStackTrace();
			}
		}
		System.out.println("CURRENT TASKS: "+TaskPool.getInstance());
	}

	private void dropOldActiveTasks() {
		ITask removeableTask = null;
		for (Iterator<ITask> iterator = TaskPool.getInstance().iterator();
			iterator.hasNext(); removeableTask = iterator.next())
			if (removeableTask!=null && removeableTask.isActive())
				iterator.remove();
	}
	
	@Override
	public void perform(TaskInfo taskInfo/*taskId*/) {
		try {
			for (ITask iTask: TaskPool.getInstance())
				if (iTask!=null)
					if (iTask.hashCode() == taskInfo.getHashCode()) {
						if (iTask.isActive()) {
							iTask.perform(taskInfo.getParameters());
							//moving to performed tasks stack
							TaskPool.getInstance().moveToQueue(iTask);
							System.out.println("_performedTasks "+TaskPool.getInstance()._performedTasks);
						}
						break;
					}
			System.out.println("UPDATENOW");
			updateNotify();
		} catch (Exception e) {
			System.out.println("COREERROR");
			CoreError performErrors = new CoreError();
			performErrors.addError(e);
			updateNotify(performErrors);
		}
	}

	@Override
	public void undo(TaskInfo taskInfo/*taskId*/) {
		try {
			ITask pollLastPerformedTask = TaskPool.getInstance().popLastPerformedTask();
			pollLastPerformedTask.undo();
			updateNotify();
		} catch (Exception e) {
			CoreError undoErrors = new CoreError();
			undoErrors.addError(e);
			updateNotify(undoErrors);
		}
	}
	
	private List<TaskInfo> getActiveTasks() {
		List<TaskInfo> rows = new LinkedList<TaskInfo>();
		for (ITask iTask : TaskPool.getInstance()) {
			if (iTask!=null) {
				TaskInfo row = new TaskInfo(iTask);
				rows.add(row);
			}
		}
		return rows;
	}
	
	private List<TaskInfo> getRows() {
		List<TaskInfo> rows = getActiveTasks();
		List<TaskInfo> performed = new LinkedList<TaskInfo>();
		for (ITask iTask : TaskPool.getInstance().getPerformedTasks()) {
			TaskInfo taskInfo = new TaskInfo(iTask);
			if (iTask.equals(TaskPool.getInstance().getLastPerformedTask()))
				taskInfo.setLastPerformed(true);
			performed.add(taskInfo);
		}
		rows.addAll(performed);
		System.out.println("CodeRefactoringModel>getRows:"+rows.toString());
		return rows;
	}

	@Override
	public void update(Observable arg0, Object arg1) {
		setChanged();
		notifyObservers(getRows());
	}

}
